#!/usr/bin/env python3

import io
import struct

class ShowIP:
    ip_file = 0
    idx_start = 0
    idx_end = 0

    def open(self, path):
        #print("Opening \"" + path + "\"")
        self.ip_file = io.open(path, "rb")
        self.idx_start = self.readLong4(0)
        self.idx_end = self.readLong4(4)

    def close():
        self.ip_file.close()
        self.ip_file = 0
        self.idx_start = 0
        self.idx_end = 0

    def readLong3(self, offset = 0):
        self.ip_file.seek(offset, io.SEEK_SET)
        buf = bytearray(4)
        self.ip_file.readinto(buf)
        buf[3] = 0
        return struct.unpack("I", buf)[0]

    def readLong4(self, offset = 0):
        self.ip_file.seek(offset, io.SEEK_SET)
        return struct.unpack("I", self.ip_file.read(4))[0]

    def int2IP(self, val):
        return  str((val  >> 24) & 0xFF)    + "." \
                +str((val >> 16) & 0xFF)    + "." \
                +str((val >> 8 ) & 0xFF)    + "." \
                +str((val)       & 0xFF)

    def IP2int(self, val):
        ip = 0
        base = 24
        for f in val.split('.'):
            if not f.isdigit():
                return -1
            if base < 0:
                return -1
            ip |= int(f) << base
            base -= 8

        if base < -8 or base > 0:
            return -1
        return ip

    def searchIPIndex(self, ip):
        lb = 0
        ub = self.idx_end - self.idx_start + 7

        while ub-lb > 7:
            mid = ub+lb
            if mid % 14 == 0:
                mid //= 2
            else:
                mid = (mid + 7) // 2
            tmp = self.readLong4(mid+self.idx_start)
            #print("lb="+str(lb)+" mid="+str(mid)+" ub="+str(ub)+" tmp="+self.int2IP(tmp))
            if ip < tmp:
                ub = mid
            else:
                lb = mid

        #print("Found lb="+str(lb)+" ip_lb="+self.int2IP(self.readLong4(lb+self.idx_start))+" ub="+str(ub)+" ip_ub="+self.int2IP(self.readLong4(ub+self.idx_start)))
        return (self.readLong3(self.idx_start+lb+4), self.readLong4(lb+self.idx_start))

    def readRecord(self, offset):
        ip_up = self.readLong4(offset)
        record = self.readL1Record(offset+4)
        if len(record) != 2:
            return ()
        return (ip_up, record[0], record[1])

    def readByte(self, offset):
        self.ip_file.seek(offset, io.SEEK_SET)
        return self.ip_file.read(1)[0]

    def readGBKString(self, offset):
        tmp = self.readByte(offset)
        length = 0
        while tmp != 0:
            tmp = self.readByte(offset+length)
            length += 1
        if length > 0:
            tmp = self.ip_file.seek(offset, io.SEEK_SET)
            buf = bytearray(length-1)
            self.ip_file.readinto(buf)
            #print(buf)
            return buf.decode("GBK")
        else:
            return ""

    def readL1Record(self, offset):
        l1_rec = ""
        l2_rec = ""
        l1_offset = offset
        l2_offset = 0
        while True:
            flag = self.readByte(l1_offset)
            if flag == 0x01:
                l1_offset = self.readLong3(l1_offset+1)
                #print("Got 0x01, jump to " + str(l1_offset))
                continue
            elif flag == 0x02:
                l2_offset = l1_offset + 4
                l1_offset = self.readLong3(l1_offset+1)
                #print("Got 0x02, jump to " + str(l1_offset))
                continue
            else:
                l1_rec = self.readGBKString(l1_offset)
                if l2_offset == 0:
                    l2_offset = l1_offset + len(l1_rec.encode("gbk")) + 1
                l2_rec = self.readL2Record(l2_offset)
                break
        return (l1_rec, l2_rec)


    def readL2Record(self, offset):
        #print("L2Record:"+str(offset))
        while True:
            flag = self.readByte(offset)
            if flag == 0x01 or flag == 0x02:
                offset = self.readLong3(offset+1)
                #print("Got special, jump to " + str(l1_offset))
                continue
            else:
                return self.readGBKString(offset)

    def showIP(self, ip):
        ip_val = self.IP2int(ip)
        if ip_val == -1:
            return ()
        idx_ip = self.searchIPIndex(ip_val)
        result = self.readRecord(idx_ip[0])
        return (self.int2IP(idx_ip[1]), self.int2IP(result[0]), result[1], result[2])

    def showIPRange(self, ip):
        pass

